//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <errno.h>
#include "CBasicFile.h"
#define IncrementDllLockCount() do {} while(0)
#define DecrementDllLockCount() do {} while(0)
#include "_CKtfsFile.cpp"

CKtfsFile::CKtfsFile()
{
    m_FileHandle = IO_INVALID_HANDLE_VALUE;
    m_wstrFileName = NULL;
}

#ifdef _GNUC
const UInt64 bias64 = 0x019db1ded53e8000LL;  //1970.01.01 - 1601.01.01
#else
const UInt64 bias64 = 0x019db1ded53e8000;     //1970.01.01 - 1601.01.01
#endif

CKtfsFile::CKtfsFile(WString wszFileName, Boolean bIsExist, Boolean bIsWritable)
{
    UInt32 AccessMode = 0, ShareMode = 0;
    int AttrMode = IO_FA_NORMAL;
    int CreateMode = 0;
    WChar * wcsFileName;

    wcsFileName = (WChar *)malloc(sizeof(WChar) * (wcslen(wszFileName) + 1));
    assert(wcsFileName);
    wcscpy(wcsFileName, wszFileName);
    m_wstrFileName = wcsFileName;
    if (bIsExist) {
        CreateMode = IO_OPEN_ALWAYS;
    }
    else {
        CreateMode = IO_CREATE_ALWAYS;
    }
    if (bIsWritable) {
        AccessMode = IO_GENERIC_READ | IO_GENERIC_WRITE;
    }
    else {
        AccessMode = IO_GENERIC_READ;
    }
    ShareMode = IO_FS_READ | IO_FS_WRITE;
    m_FileHandle = IO_CreateFile(wcsFileName, AccessMode,
                        ShareMode, CreateMode,  AttrMode, NULL);

    if (m_FileHandle == IO_INVALID_HANDLE_VALUE) {
        kprintf("Create Ktfs File Fail \n");
        return;
    }
    FILETIME CreationTime;
    FILETIME LastAccessTime;
    FILETIME LastWriteTime;
    UInt64 tmpint;

    IO_GetFileTime(m_FileHandle, &CreationTime,
        &LastAccessTime, &LastWriteTime);
    tmpint = *(UInt64*)&CreationTime;
    m_iCreateTime = (UInt32)((tmpint - bias64) / 10000000);
    tmpint = *(UInt64*)&LastAccessTime;
    m_iAccessTime = (UInt32)((tmpint - bias64) / 10000000);
    tmpint = *(UInt64*)&LastWriteTime;
    m_iModificationTime = (UInt32)((tmpint - bias64) / 10000000);
    m_eFMode = FileAccessMode_ReadWrite;
}

CKtfsFile::~CKtfsFile()
{
    if (NULL != m_wstrFileName) {
        free((void*)(const wchar_t*)m_wstrFileName);
    }
    if ((NULL != m_FileHandle)
        && (IO_INVALID_HANDLE_VALUE != m_FileHandle)) {
        IO_CloseHandle(m_FileHandle);
    }
}

ECode CKtfsFile::Read(
                /* [in] */ Int32 offset,
                /* [in] */ Int32 toRead,
                /* [out] */ MemoryBuf * pBuf)
{
    UInt32 iSizeRead = 0;
    void* pBuffer = NULL;

    if (pBuf == NULL || toRead > pBuf->GetCapacity() ||
        /*toRead < 0 || */toRead > 0x7fffffff) {
        return ECODE_FROM_ERRNO(EINVAL);
    }

    Int32 orgSize = 0;
    GetSize(&orgSize);
    if (/*offset < 0 || */offset > orgSize) {
        return ECODE_FROM_ERRNO(EINVAL);
    }

    pBuffer = pBuf->GetPayload();

    UInt32 SetPointRet;
    SetPointRet = IO_SetFilePointer(m_FileHandle, offset, NULL, IO_FILE_BEGIN);
    if (SetPointRet == IO_INVALID_SET_FILE_POINTER) {
        return E_INVALID_POINTER;
    }
    if (IO_ReadFile(m_FileHandle, pBuffer, toRead, &iSizeRead)) {
        pBuf->SetUsed(iSizeRead);
        return NOERROR;
    }

    return E_FAIL;
}

ECode CKtfsFile::Write(
                /* [in] */ Int32 offset,
                /* [in] */ const MemoryBuf & ezbBuf,
                /* [out] */ Int32 * pWrite)
{
    if (m_eFMode & FileAccessMode_Read) {
        return ECODE_FROM_ERRNO(EACCES);
    }

    int Size = ezbBuf.GetUsed();
    if (Size > 0x7fffffff) {
        return ECODE_FROM_ERRNO(EINVAL);
    }
    if (offset + Size > 0x70000000) {
        return ECODE_FROM_ERRNO(EFBIG);
    }

    Int32 orgSize = 0;
    GetSize(&orgSize);
    if ((offset > orgSize) || (offset + Size) > orgSize) {
        SetSize(offset + Size);
    }

    UInt32 SetPointRet;
    SetPointRet = IO_SetFilePointer(m_FileHandle, offset, NULL, IO_FILE_BEGIN);
    if (SetPointRet == IO_INVALID_SET_FILE_POINTER) {
        return E_INVALID_POINTER;
    }
    if (IO_WriteFile(m_FileHandle, (char *)ezbBuf.GetPayload(),
        Size, (UInt32 *)pWrite)) {
        return NOERROR;
    }
    return S_FALSE;
}

ECode CKtfsFile::GetSize(
                /* [out] */ Int32 * pSize)
{
    if (!pSize) {
        return E_INVALID_ARGUMENT;
    }

    *pSize = IO_GetFileSize(m_FileHandle, NULL);
    return NOERROR;
}

ECode CKtfsFile::SetSize(
                /* [in] */ Int32 size)
{
    if (m_eFMode & FileAccessMode_Read) {
        return ECODE_FROM_ERRNO(EACCES);
    }

    UInt32 pos = IO_SetFilePointer(m_FileHandle, size, NULL, IO_SEEK_SET);
    if (pos == 0xffffffff) {
        return E_FAIL;
    }
    if (IO_SetEndOfFile(m_FileHandle))
        return NOERROR;
    else {
        return E_FAIL;
    }
}

ECode CKtfsFile::GetOptimalBlockSize(
                /* [out] */ Int32 * pSize)
{
    if (!pSize) {
        return E_INVALID_ARGUMENT;
    }
    *pSize = 4096;
    return NOERROR;
}

ECode CKtfsFile::Sync()
{
    return NOERROR;
}

ECode CKtfsFile::GetType(
                /* [out] */ FileType * pType)
{
    if (!pType) {
        return E_INVALID_ARGUMENT;
    }

    *pType = FileType_RegularFile;
    return NOERROR;
}

ECode CKtfsFile::GetAccessTime(
                /* [out] */ Int32 * pTime)
{
    if (!pTime) {
        return E_INVALID_ARGUMENT;
    }

    *pTime = m_iAccessTime;
    return NOERROR;
}

ECode CKtfsFile::SetAccessTime(
                /* [in] */ Int32 time)
{
    if (m_eFMode & FileAccessMode_Read) {
        return ECODE_FROM_ERRNO(EACCES);
    }

    m_iAccessTime = time;
    return NOERROR;
}

ECode CKtfsFile::GetModificationTime(
                /* [out] */ Int32 * pTime)
{
    *pTime = m_iModificationTime;
    return NOERROR;
}

ECode CKtfsFile::SetModificationTime(
                /* [in] */ Int32 time)
{
    if (m_eFMode & FileAccessMode_Read) {
        return ECODE_FROM_ERRNO(EACCES);
    }

    m_iModificationTime = time;
    return NOERROR;
}

ECode CKtfsFile::GetCreateTime(
                /* [out] */ Int32 * pTime)
{
    if (!pTime) {
        return E_INVALID_ARGUMENT;
    }

    *pTime = m_iCreateTime;
    return NOERROR;
}

ECode CKtfsFile::CheckAccessMode(
                /* [in] */ FileAccessMode mode)
{
    if (m_eFMode == mode) {
        return NOERROR;
    }
    else {
        return S_FALSE;
    }
}

ECode CKtfsFile::GetAccessMode(
                /* [out] */ FileAccessMode * pMode)
{
    if (!pMode) {
        return E_INVALID_ARGUMENT;
    }

    *pMode = m_eFMode;
    return NOERROR;
}

ECode CKtfsFile::SetAccessMode(
                /* [in] */ FileAccessMode mode)
{
    const wchar_t* wcsName = m_wstrFileName;
    UInt32 attr = IO_GetFileAttributes (wcsName);
    if (mode == FileAccessMode_Read) {
        attr |= IO_FA_READONLY;
    }
    else {
        attr &= ~IO_FA_READONLY;
    }

    IO_SetFileAttributes (wcsName, attr);
    m_eFMode = mode;
    return NOERROR;
}

ECode CKtfsFile::GetShareMode(
                /* [out] */ FileShareMode * pMode)
{
    return E_NOT_IMPLEMENTED;
}

ECode CKtfsFile::SetShareMode(
                /* [in] */ FileShareMode mode)
{
    return E_NOT_IMPLEMENTED;
}

ECode CKtfsFile::AddFileMonitor(
                /* [out] */ IEvent** ppEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CKtfsFile::RemoveFileMonitor(
                /* [in] */ IEvent * pEvent)
{
    return E_NOT_IMPLEMENTED;
}
